/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "Shared/Net/Socket.h"
#include "Shared/Util/Error.h"
#include "Shared/Net/SocketEventHandler.h"
#include "Shared/Net/TcpServer.h"
using namespace Shared::Util;

namespace Shared {
namespace Net {

    Socket::Socket(void)
        : _id(-1)
        , _fd(-1)
        , _bev(NULL)
        , _evHandler(NULL)
        , _ip("")
        , _port(0)
        , _errString("")
        , _errCode(0)
        , _fatalError(false)
        , _owner(NULL)
		, _thread(NULL)
    {

    }

    Socket::~Socket(void)
    {
        unintialize();
    }

    void Socket::close(void)
    {
        SHARED_ASSERT(_owner != NULL);
        _owner->closeSocket(this);
    }

    void Socket::send(const std::uint8_t *data, std::size_t size)
    {
        if (!_fatalError)
        {
            bufferevent_enable(_bev, EV_WRITE);
            if (bufferevent_write(_bev, data, size) != 0) {
                onError();
            }
        }
    }

    std::size_t Socket::receiveSize(void)
    {
        return evbuffer_get_length(bufferevent_get_input(_bev));
    }

    int Socket::receive(std::uint8_t *data, std::size_t size)
    {
        return evbuffer_remove(bufferevent_get_input(_bev), data, size);
    }

    int Socket::receivePeek(std::uint8_t *data, std::size_t size)
    {
        return evbuffer_copyout(bufferevent_get_input(_bev), data, size);
    }

    std::size_t Socket::receiveSkip(std::size_t size)
    {
        if (evbuffer_drain(bufferevent_get_input(_bev), size) == 0) {
            return size;
        }
        return 0;
    }

    void Socket::enableRead(bool enable) {
        if (enable) {
            bufferevent_enable(_bev, EV_READ);
        } else {
            bufferevent_disable(_bev, EV_READ);
        }
    }

    void Socket::enableWrite(bool enable) {
        if (enable) {
            bufferevent_enable(_bev, EV_WRITE);
        } else {
            bufferevent_disable(_bev, EV_WRITE);
        }
    }

    void Socket::unintialize(void)
    {
        if (_bev)
        {
            bufferevent_disable(_bev, EV_READ);
            bufferevent_free(_bev);
            _bev = NULL;
            _fd = -1;
        }
    }

    void Socket::onError(void)
    {
        if (_fatalError) {
            return;
        }
        _fatalError = true;

        enableWrite(false);
    }

    void Socket::readCallback(struct bufferevent *, void *userData)
    {
        Socket *pSock = static_cast<Socket*>(userData);
        SHARED_ASSERT(pSock);
        pSock->_evHandler->onRead();
    }

    void Socket::writeCallback(struct bufferevent *, void *userData)
    {
        Socket *pSock = static_cast<Socket*>(userData);
        SHARED_ASSERT(pSock);

        if (pSock->_fatalError)
        {
            pSock->enableWrite(false);
            pSock->_evHandler->onWrite();
        }
    }

    void Socket::eventCallback(struct bufferevent *, short events, void *userData)
    {
        Socket *pSock = static_cast<Socket*>(userData);
        SHARED_ASSERT(pSock);

        if (events & BEV_EVENT_EOF) {
            pSock->onError();
            pSock->_evHandler->onEof();
        }
        else if (events & BEV_EVENT_ERROR) {
            pSock->_errCode = lastNetError();
            getErrorString(pSock->_errString, pSock->_errCode);
            pSock->onError();
            pSock->_evHandler->onError();
        }
        else if (events & BEV_EVENT_TIMEOUT) {
            int flags = 0;
            if (events & BEV_EVENT_READING) {
                flags |= BEV_EVENT_READING;
            }
            if (events & BEV_EVENT_WRITING) {
                flags |= BEV_EVENT_WRITING;
            }
            pSock->_evHandler->onTimeout(flags);
        }
    }

}}